/*
 * Copyright 2024 The CHANGLEI Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import 'package:recase/recase.dart';

const _underline = r'_';

extension NamedString on String {
  Name get named => Name._(isEmpty ? Name.unnamed : this);

  ReCase get reCase {
    var s = this;
    s = s.trim().replaceAll(RegExp(r'[^_$a-zA-Z0-9]'), _underline);
    if (!s.startsWith(RegExp(r'[a-zA-Z]'))) {
      s = 'a$_underline$s';
    }
    return ReCase(s);
  }

  /// PascalCase
  Name get pascalCased => reCase.pascalCase.named;

  /// snake_case
  Name get snakeCased => reCase.snakeCase.named;

  /// Header-Case
  Name get headerCased => reCase.headerCase.named;

  /// Title Case
  Name get titleCased => reCase.titleCase.named;

  /// camelCase
  Name get camelCased => reCase.camelCase.named;
}

/// Created by changlei on 2024/4/26.
///
/// 生成类名和字段名
extension type const Name._(String _) implements String {
  Name.forClass(String value) : this._(value.pascalCased);

  Name.forField(String value) : this._(value.snakeCased);

  static const unnamed = Name._(r'unnamed');

  static const string = Name._(r'String');

  static const underline = Name._(r'_');

  static const empty = Name._(r'');
}

final class NameGenerator {
  final _names = <String, int>{};

  Name classOfAll(Iterable<String> elements) => classOf(elements.join(_underline));

  Name classOf(String value) => _generate(Name.forClass(value));

  Name fieldOf(String value) => _generate(Name.forField(value));

  Name _generate(String name) {
    name = name.named;
    final nameCount = _names[name] ?? 0;
    final oldName = name;
    if (nameCount > 0) {
      name = '$name$nameCount';
    }
    _names[oldName] = nameCount + 1;
    return Name._(name);
  }
}
